<?php
/**
 * Interprets the URL and sets the corresponding query variables
 *
 * @package Concerto
 * @subpackage System
 * @author Jason Raede <jason@torchdm.com>
 */
namespace application\system;

class UrlInterpreter {
	
	/**
	 * Current URI exploded on '/'
	 *
	 * @var array
	 */
	protected $workingUrl;
	
	/**
	 * @deprecated
	 */
	protected $permalinkMatch = FALSE;
	
	/**
	 * Array of information about each URL part
	 *
	 * Array is structured as follows
	 * * 'resource'=>The resource type
	 * * 'id'=>The ID of the resource
	 * * 'url'=>The URL that matched that resource
	 *
	 * @var array
	 */
	public $urlInfo = array();
	
	/**
	 * @var string
	 */
	public $currentUrl;
	
	
	public function __construct() {
		$port = (CN_URL_PORT) ? (($_SERVER['SERVER_PORT']) ? ':'.$_SERVER['SERVER_PORT'] : '') : '';

		$this->currentUrl = (!empty($_SERVER['HTTPS'])) ? "https://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] : "http://".$_SERVER['SERVER_NAME'].$port.$_SERVER['REQUEST_URI'];
		
		
	}
	
	/**
	 * Parse URL to get exactly what we want to interpret
	 */
	protected function parseUrl() {
		/** Filter out site url */
		$workingUrl = $this->currentUrl;
		if(strpos($workingUrl, CN_URL) !== FALSE) {
			$workingUrl = str_replace(CN_URL, '', $workingUrl);
		}
		
		/** Filter out # (used for ajax) */
		$workingUrl = str_replace('/#', '', $workingUrl);
		
		/** Filter out query */
		if(preg_match('/\?(.)+/', $workingUrl, $matches)) $workingUrl = str_replace($matches[0], '', $workingUrl);
		/** Get rid of unwanted / at the end */
		if(substr($workingUrl, -1) == '/') $workingUrl = substr($workingUrl, 0, strlen($workingUrl) - 1);
		$workingUrl = str_replace('index.php', '', $workingUrl);
		if($workingUrl) $this->workingUrl = explode('/', $workingUrl);
		
	}
	
	/**
	 * Main function for the class - called on index.php when first instantiating Concerto
	 */
	public function interpret() {
		global $concerto;
		$queryVars = array();
		$this->parseUrl();
		if(!$this->workingUrl) {
			if(getQueryVar('form')) setQueryVar('view', 'form');
			elseif(!getQueryVar('view')) setQueryVar('view', 'content');
			return null;
		}
		
		
		
		/**
		 * Check indeces of $this->workingUrl in order
		 *
		 * Indeces can be:
		 * * Content Identifier (for ID or geneology)
		 * * Content Type
		 * * Tag or tag group
		 * * Year or month for archiving
		 * * System reference (forms, etc)
		 * * "feed" (for RSS feed rendering, as opposed to template rendering)
		 */
		$functionOrder = array('checkSystem', 'checkMatchesContent', 'checkMatchesContentType', 'checkMatchesTag', 'checkMatchesYear', 'checkMatchesMonth', 'checkFeed');
		foreach($this->workingUrl as $url) {
			for($i=0;$i<6;$i++) {
				if($this->{$functionOrder[$i]}($url)) {
					$matched = TRUE;
					break;
				}
				
			}
			if(!$matched) {
				$concerto->Loader->loadError(ERROR_CONTENT_NOT_FOUND);
			}
		}
		if(count($this->workingUrl) && getQueryVar('view') == 'template' && (!$concerto->queryVars['type'] && $concerto->queryVars['display'] != 'user')) {
			$concerto->Loader->loadError(ERROR_CONTENT_NOT_FOUND);
		}
		
		if(getQueryVar('form')) {
			setQueryVar('view', 'form');
		}
		
		if(!getQueryVar('view')) setQueryVar('view', 'template');
		
		
		
		
	}
	
	/**
	 * Check if the url matches a system url
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkSystem($url) {
		global $concerto;
		// Some system pages must be accessed with no other URL variables
		if(count($this->workingUrl) == 1) {
			switch($url) {
				case 'login':
					setQueryVar('view', 'form');
					self::setQueryVar('form', 'login');
					$this->urlInfo[] = array('resource'=>'form', 'id'=>'login', 'url'=>$url);
					return TRUE;
					break;
				case 'signup':
				case 'sign-up':
				case 'register':
					setQueryVar('view', 'form');
					self::setQueryVar('form', 'signup');
					$this->urlInfo[] = array('resource'=>'form', 'id'=>'signup', 'url'=>$url);
					return TRUE;
					break;
				case 'add-user':
					setQueryVar('view', 'form');
					self::setQueryVar('form', 'add_user');
					$this->urlInfo[] = array('resource'=>'form', 'id'=>'add_user', 'url'=>$url);
					return TRUE;
					break;
				case 'user':
					setQueryVar('view', 'template');
					setQueryVar('display', 'user');
					return TRUE;
					break;
				case 'admin':
					setQueryVar('view', 'admin');
					$this->urlInfo[] = array('resource'=>'admin', 'url'=>$url);
					return TRUE;
					break;
				case '404':
					setQueryVar('view', 'error');
					return TRUE;
					break;
				default:
					if(self::checkAddForm($url)) {
						return TRUE;
					}
					break;
			}
		}
		elseif(count($this->workingUrl) == 2 && $this->workingUrl[0] == 'ajax') { // For now, just ajax requests
			if($url == 'ajax') {
				setQueryVar('view', 'ajax');
				return TRUE;
			}
			elseif($url == 'content' || $url == 'user') {
				self::setQueryVar('display', $url);
				return TRUE;
			}
			else {
				$concerto->Loader->loadError(ERROR_CONTENT_NOT_FOUND);	
			}
		}
		else {
			// Only other system permalinks are "edit", "add-content-type", and "ajax"
			if($url == 'edit') {
				setQueryVar('view', 'form');
				self::setQueryVar('form', 'edit_content');
				$this->urlInfo[] = array('resource'=>'form', 'id'=>'edit', 'url'=>$url);
				return TRUE;
			}
			elseif($this->checkAddForm($url)) {
				return TRUE;
			}
			
		}
		return FALSE;
	}
	
	/**
	 * Check if the URL is referencing a form for adding a content
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkAddForm($url) {
		global $concerto;
		$function = substr($url, 0, 4);
		$typeUrl = substr($url, 4);
		$type = \application\models\ContentType::matchesTypeSingular($typeUrl);
		if($function == 'add-' && $type) {
			$concerto->view = 'form';
			self::setQueryVar('form', 'add_content');
			self::setQueryVar('type', $type);
			$this->urlInfo[] = array('resource'=>'form', 'id'=>'add', 'type'=>$type, 'url'=>$url);
			return TRUE;
			
		}
		return FALSE;
	}
	
	/**
	 * Set a query var and throw an error if it's already been set
	 *
	 * @param string $var
	 * @param mixed $value
	 * @param bool $check Set to FALSE to bypass redundancy check
	 */
	protected static function setQueryVar($var, $value, $check = TRUE) {
		global $concerto;
		if($check) {
			$prev = $concerto->queryVars[$var];
			if($var == 'tags') {
				if($prev) {
					setQueryVar('tags', $prev.','.$value);
				}
				else {
					setQueryVar('tags', $value);
				}
			}
			else {
				if($prev && ($prev != $value)) {
					$concerto->Loader->loadError(ERROR_UNMATCHED_URL_QUERY);
				}
				else {
					$concerto->queryVars[$var] = $value;
				}
			}
		}
		else {
			$concerto->queryVars[$var] = $value;
		}
	}
	
	/**
	 * Check if the URL matches a content object
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkMatchesContent($url) {
		global $concerto;
		$content = $concerto->DB->getRow("SELECT `content_id`, `content_type` FROM `cn_content` WHERE `content_permalink` = '$url'");
		if(!$content) $content = DB()->getRow("SELECT `content_id`, `content_type` FROM `cn_content` WHERE `content_id`='$url'");
		if($content->content_id) {
			// Check for geneology
			if($id = $concerto->queryVars['id']) {
				// Check if this new content id is a descendant of the older $id
				$generation = \application\models\Content::isDescendantOfContent($content->content_id, $id);
				if($generation) {
					switch($generation) {
						case 1:
							self::setQueryVar('childOf', $id, FALSE);
							self::setQueryVar('id', $content->content_id, FALSE);
							self::setQueryVar('type', $content->content_type, FALSE);
							break;
						case 2:
							self::setQueryVar('grandchildOf', $id, FALSE);
							self::setQueryVar('id', $content->content_id, FALSE);
							self::setQueryVar('type', $content->content_type, FALSE);
							break;
						case 3:
							self::setQueryVar('greatGrandchildOf', $id, FALSE);
							self::setQueryVar('id', $content->content_id, FALSE);
							self::setQueryVar('type', $content->content_type, FALSE);
							break;
						default:
							$concerto->Loader->loadError(ERROR_UNMATCHED_PERMALINK_GENEOLOGY);
							break;
					}
				}
				else {
					$concerto->Loader->loadError(ERROR_UNMATCHED_PERMALINK_GENEOLOGY);
				}
				
			}
			else {
				self::setQueryVar('id', $content->content_id);
				self::setQueryVar('type', $content->content_type);
			}
			
			$this->urlInfo[] = array('resource'=>'content', 'id'=>$content->content_id, 'type'=>$content->content_type, 'url'=>$url);
			self::setQueryVar('display', 'content');
			return TRUE;	
		}
		return FALSE;
	}
	
	/**
	 * Check if the URL matches a content type
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkMatchesContentType($url) {
		global $concerto;
		$type = $concerto->DB->getVar("SELECT `ct_id` FROM `cn_content_types` WHERE `ct_permalink` = '$url'");
		if($type) {
			self::setQueryVar('type', $type);
			self::setQueryVar('display', 'content');
			$this->urlInfo[] = array('resource'=>'type', 'id'=>$type, 'url'=>$url);
			return TRUE;	
		}
		return FALSE;
	}
	
	/**
	 * Check if the URL matches a tag or tag group
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkMatchesTag($url) {
		global $concerto;
		$tag = $concerto->DB->getRow("SELECT `tag_id`, `tag_type`, `tag_content_type` FROM `cn_tags` WHERE `tag_permalink` = '$url'");
		if($tag->tag_id) {
			if($tag->tag_type == 'group') {
				self::setQueryVar('tagGroup', $tag->tag_id);
				self::setQueryVar('type', $tag->tag_content_type);
				$this->urlInfo[] = array('resource'=>'tag_group', 'id'=>$tag->tag_id, 'url'=>$url);
			}
			else {
				self::setQueryVar('tags', $tag->tag_id);
				self::setQueryVar('type', $tag->tag_content_type);
				self::setQueryVar('display', 'content');
				$this->urlInfo[] = array('resource'=>'tag', 'id'=>$tag->tag_id, 'url'=>$url);
			}
			
			return TRUE;	
		}
		return FALSE;
	}
	
	/**
	 * Check if the URL is a year
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkMatchesYear($url) {
		if(is_numeric($url) && strlen($url) == 4) {
			self::setQueryVar('year', $url);
			$this->urlInfo[] = array('resource'=>'archive_year', 'url'=>$url);
			return TRUE;
		}
		return FALSE;
	}
	
	/**
	 * @deprecated Too confusing to have days
	 */
	protected function checkMatchesDay($url) {
		if(is_numeric($url) && $url <=31) {
			self::setQueryVar('day', $url);
			$this->urlInfo[] = array('resource'=>'archive_day', 'url'=>$url);
			return TRUE;
		}
		return FALSE;
	}
	
	/**
	 * Check if the URL matches a month
	 *
	 * @param string $url
	 * @return bool
	 */
	protected function checkMatchesMonth($url) {
		if(getQueryVar('month')) return FALSE;
		$months = array('january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
		$shortMonths = array('jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec');
		if(is_numeric($url) && strlen($url) == 2) {
			self::setQueryVar('month', $url);
			$this->urlInfo[] = array('resource'=>'archive_month', 'url'=>$url);
			return TRUE;
		}
		elseif(in_array($url, $months)) {
			$month = array_search($url, $months) + 1;
			self::setQueryVar('month', $month);
			$this->urlInfo[] = array('resource'=>'archive_month', 'url'=>$url);
			return TRUE;
		}
		elseif(in_array($url, $shortMonths)) {
			$month = array_search($url, $shortMonths) + 1;
			self::setQueryVar('month', $month);
			$this->urlInfo[] = array('resource'=>'archive_month', 'url'=>$url);
			return TRUE;
		}
		return FALSE;
	}
	
	/**
	 * Check if the URL is for an RSS feed
	 * 
	 * @param string $url
	 * @return bool
	 */
	protected function checkFeed($url) {
		/** Check for RSS feeds */
		if($url == 'feed') {
			self::setQueryVar('display', 'feed');
			return TRUE;
		}
		return FALSE;
	}
	
	

}
?>